/* * Copyright 2012-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.boot.autoconfigure.jdbc; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverPropertyInfo; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.Properties; import java.util.Random; import java.util.logging.Logger; import javax.sql.DataSource; import com.zaxxer.hikari.HikariDataSource; import org.apache.commons.dbcp2.BasicDataSource; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.BeanCreationException; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.jdbc.DatabaseDriver; import org.springframework.boot.test.util.EnvironmentTestUtils; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.SimpleDriverDataSource; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; /** * Tests for {@link DataSourceAutoConfiguration}. * * @author Dave Syer * @author Stephane Nicoll */ public class DataSourceAutoConfigurationTests { private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); @Before public void init() { EmbeddedDatabaseConnection.override = null; EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.initialize:false", "spring.datasource.url:jdbc:hsqldb:mem:testdb-" + new Random().nextInt()); } @After public void restore() { EmbeddedDatabaseConnection.override = null; this.context.close(); } @Test public void testDefaultDataSourceExists() throws Exception { this.context.register(DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(DataSource.class)).isNotNull(); } @Test public void testDataSourceHasEmbeddedDefault() throws Exception { this.context.register(DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); HikariDataSource dataSource = this.context.getBean(HikariDataSource.class); assertThat(dataSource.getJdbcUrl()).isNotNull(); assertThat(dataSource.getDriverClassName()).isNotNull(); } @Test(expected = BeanCreationException.class) public void testBadUrl() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.url:jdbc:not-going-to-work"); EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE; this.context.register(DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(DataSource.class)).isNotNull(); } @Test(expected = BeanCreationException.class) public void testBadDriverClass() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.driverClassName:org.none.jdbcDriver", "spring.datasource.url:jdbc:hsqldb:mem:testdb"); EmbeddedDatabaseConnection.override = EmbeddedDatabaseConnection.NONE; this.context.register(DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBean(DataSource.class)).isNotNull(); } @Test public void hikariValidatesConnectionByDefault() throws Exception { HikariDataSource dataSource = autoConfigureDataSource(HikariDataSource.class, "org.apache.tomcat"); assertThat(dataSource.getConnectionTestQuery()).isNull(); // Use Connection#isValid() } @Test public void tomcatIsFallback() throws Exception { org.apache.tomcat.jdbc.pool.DataSource dataSource = autoConfigureDataSource( org.apache.tomcat.jdbc.pool.DataSource.class, "com.zaxxer.hikari"); assertThat(dataSource.getUrl()).isEqualTo("jdbc:hsqldb:mem:testdb"); } @Test public void tomcatValidatesConnectionByDefault() { org.apache.tomcat.jdbc.pool.DataSource dataSource = autoConfigureDataSource( org.apache.tomcat.jdbc.pool.DataSource.class, "com.zaxxer.hikari"); assertThat(dataSource.isTestOnBorrow()).isTrue(); assertThat(dataSource.getValidationQuery()) .isEqualTo(DatabaseDriver.HSQLDB.getValidationQuery()); } @Test public void commonsDbcp2IsFallback() throws Exception { BasicDataSource dataSource = autoConfigureDataSource(BasicDataSource.class, "com.zaxxer.hikari", "org.apache.tomcat"); assertThat(dataSource.getUrl()).isEqualTo("jdbc:hsqldb:mem:testdb"); } @Test public void commonsDbcp2ValidatesConnectionByDefault() throws Exception { org.apache.commons.dbcp2.BasicDataSource dataSource = autoConfigureDataSource( org.apache.commons.dbcp2.BasicDataSource.class, "com.zaxxer.hikari", "org.apache.tomcat"); assertThat(dataSource.getTestOnBorrow()).isEqualTo(true); assertThat(dataSource.getValidationQuery()).isNull(); // Use Connection#isValid() } @Test public void testEmbeddedTypeDefaultsUsername() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.driverClassName:org.hsqldb.jdbcDriver", "spring.datasource.url:jdbc:hsqldb:mem:testdb"); this.context.register(DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); DataSource bean = this.context.getBean(DataSource.class); assertThat(bean).isNotNull(); @SuppressWarnings("resource") HikariDataSource pool = (HikariDataSource) bean; assertThat(pool.getDriverClassName()).isEqualTo("org.hsqldb.jdbcDriver"); assertThat(pool.getUsername()).isEqualTo("sa"); } /** * This test makes sure that if no supported data source is present, a datasource is * still created if "spring.datasource.type" is present. */ @Test public void explicitTypeNoSupportedDataSource() { EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.driverClassName:org.hsqldb.jdbcDriver", "spring.datasource.url:jdbc:hsqldb:mem:testdb", "spring.datasource.type:" + SimpleDriverDataSource.class.getName()); this.context.setClassLoader( new HidePackagesClassLoader("org.apache.tomcat", "com.zaxxer.hikari", "org.apache.commons.dbcp", "org.apache.commons.dbcp2")); testExplicitType(); } @Test public void explicitTypeSupportedDataSource() { EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.driverClassName:org.hsqldb.jdbcDriver", "spring.datasource.url:jdbc:hsqldb:mem:testdb", "spring.datasource.type:" + SimpleDriverDataSource.class.getName()); testExplicitType(); } private void testExplicitType() { this.context.register(DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertThat(this.context.getBeansOfType(DataSource.class)).hasSize(1); DataSource bean = this.context.getBean(DataSource.class); assertThat(bean).isNotNull(); assertThat(bean.getClass()).isEqualTo(SimpleDriverDataSource.class); } @Test public void testExplicitDriverClassClearsUsername() throws Exception { EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.driverClassName:" + "org.springframework.boot.autoconfigure.jdbc." + "DataSourceAutoConfigurationTests$DatabaseTestDriver", "spring.datasource.url:jdbc:foo://localhost"); this.context.register(DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); DataSource bean = this.context.getBean(DataSource.class); assertThat(bean).isNotNull(); HikariDataSource pool = (HikariDataSource) bean; assertThat(pool.getDriverClassName()).isEqualTo( "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfigurationTests$DatabaseTestDriver"); assertThat(pool.getUsername()).isNull(); } @Test public void testDefaultDataSourceCanBeOverridden() throws Exception { this.context.register(TestDataSourceConfiguration.class, DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); DataSource dataSource = this.context.getBean(DataSource.class); assertThat(dataSource).isInstanceOf(BasicDataSource.class); } @SuppressWarnings("unchecked") private <T extends DataSource> T autoConfigureDataSource(Class<T> expectedType, final String... hiddenPackages) { EnvironmentTestUtils.addEnvironment(this.context, "spring.datasource.driverClassName:org.hsqldb.jdbcDriver", "spring.datasource.url:jdbc:hsqldb:mem:testdb"); this.context.setClassLoader(new HidePackagesClassLoader(hiddenPackages)); this.context.register(DataSourceAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); DataSource bean = this.context.getBean(DataSource.class); assertThat(bean).isInstanceOf(expectedType); return (T) bean; } @Configuration static class TestDataSourceConfiguration { private BasicDataSource pool; @Bean public DataSource dataSource() { this.pool = new BasicDataSource(); this.pool.setDriverClassName("org.hsqldb.jdbcDriver"); this.pool.setUrl("jdbc:hsqldb:target/overridedb"); this.pool.setUsername("sa"); return this.pool; } } // see testExplicitDriverClassClearsUsername public static class DatabaseTestDriver implements Driver { @Override public Connection connect(String url, Properties info) throws SQLException { return mock(Connection.class); } @Override public boolean acceptsURL(String url) throws SQLException { return true; } @Override public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) throws SQLException { return new DriverPropertyInfo[0]; } @Override public int getMajorVersion() { return 1; } @Override public int getMinorVersion() { return 0; } @Override public boolean jdbcCompliant() { return false; } @Override public Logger getParentLogger() throws SQLFeatureNotSupportedException { return mock(Logger.class); } } }